<?php

namespace seecms;

class SeeRequest
{
    /**
     * @var SeeConfig|null
     */
    protected static $instance = null;

    /**
     * @var array
     */
    protected $params = [];

    protected $method = '';

    protected $filter;

    protected function __construct()
    {

    }

    protected function __clone()
    {

    }

    /**
     * @return SeeRequest
     */
    public static function instance(): SeeRequest
    {
        if (is_null(self::$instance)) {
            self::$instance = new self();
        }

        self::$instance->method = strtoupper(self::$instance->method());

        return self::$instance;
    }

    /**
     * 获取原始请求类型
     * @return mixed|string
     */
    public function method()
    {
        return ($_SERVER['REQUEST_METHOD'] ?? '') ?: 'GET';
    }

    protected function getData(array $data, string $name, $default = null)
    {
        foreach (explode('.', $name) as $val) {
            if (isset($data[$val])) {
                $data = $data[$val];
            } else {
                return $default;
            }
        }

        return $data;
    }

    protected function typeCast(&$data, string $type)
    {
        switch (strtolower($type)) {
            // 数组
            case 'a':
                $data = (array)$data;
                break;
            // 数字
            case 'd':
                $data = (int)$data;
                break;
            // 浮点
            case 'f':
                $data = (float)$data;
                break;
            // 布尔
            case 'b':
                $data = (boolean)$data;
                break;
            // 字符串
            case 's':
                if (is_scalar($data)) {
                    $data = (string)$data;
                } else {
                    throw new \InvalidArgumentException('variable type error：' . gettype($data));
                }
                break;
        }
    }

    protected function getFilter($filter, $default): array
    {
        if (is_null($filter)) {
            $filter = [];
        } else {
            $filter = $filter ?: $this->filter;
            if (is_string($filter) && false === strpos($filter, '/')) {
                $filter = explode(',', $filter);
            } else {
                $filter = (array)$filter;
            }
        }

        $filter[] = $default;

        return $filter;
    }

    /**
     * 递归过滤给定的值
     * @access public
     * @param mixed $value 键值
     * @param mixed $key 键名
     * @param array $filters 过滤方法+默认值
     * @return mixed
     */
    public function filterValue(&$value, $key, $filters)
    {
        $default = array_pop($filters);

        foreach ($filters as $filter) {
            if (is_callable($filter)) {
                // 调用函数或者方法过滤
                if (is_null($value)) {
                    continue;
                }

                $value = call_user_func($filter, $value);
            } elseif (is_scalar($value)) {
                if (is_string($filter) && false !== strpos($filter, '/')) {
                    // 正则过滤
                    if (!preg_match($filter, $value)) {
                        // 匹配不成功返回默认值
                        $value = $default;
                        break;
                    }
                } elseif (!empty($filter)) {
                    // filter函数不存在时, 则使用filter_var进行过滤
                    // filter为非整形值时, 调用filter_id取得过滤id
                    $value = filter_var($value, is_int($filter) ? $filter : filter_id($filter));
                    if (false === $value) {
                        $value = $default;
                        break;
                    }
                }
            }
        }

        return $value;
    }

    protected function filterData($data, $filter, $name, $default)
    {
        // 解析过滤器
        $filter = $this->getFilter($filter, $default);

        if (is_array($data)) {
            array_walk_recursive($data, [$this, 'filterValue'], $filter);
        } else {
            $this->filterValue($data, $name, $filter);
        }

        return $data;
    }

    public function input(array $data = [], $name = '', $default = null, $filter = '')
    {
        if (false === $name) {
            // 获取原始数据
            return $data;
        }

        $name = (string)$name;
        if ('' != $name) {
            // 解析name
            if (strpos($name, '/')) {
                [$name, $type] = explode('/', $name);
            }

            $data = $this->getData($data, $name);

            if (is_null($data)) {
                return $default;
            }

            if (is_object($data)) {
                return $data;
            }
        }

        $data = $this->filterData($data, $filter, $name, $default);

        if (isset($type) && $data !== $default) {
            // 强制类型转换
            $this->typeCast($data, $type);
        }

        return $data;
    }

    public function param($name = '', $default = null, $filter = '')
    {

        $method = $this->method();

        // 自动获取请求变量
        switch ($method) {
            case 'POST':
                $vars = $this->post(false);
                break;
            case 'PUT':
            case 'DELETE':
            case 'PATCH':
                $vars = $this->put(false);
                break;
            default:
                $vars = [];
        }

        // 当前请求参数和URL地址中的参数合并
        $param = array_merge($this->get(false), $vars);

        if (is_array($name)) {
            return $this->only($name, $param, $filter);
        }

        return $this->input($param, $name, $default, $filter);
    }

    public function only(array $name, $data = 'param', $filter = ''): array
    {
        $data = is_array($data) ? $data : $this->$data();

        $item = [];
        foreach ($name as $key => $val) {

            if (is_int($key)) {
                $default = null;
                $key = $val;
                if (!key_exists($key, $data)) {
                    continue;
                }
            } else {
                $default = $val;
            }

            $item[$key] = $this->filterData($data[$key] ?? $default, $filter, $key, $default);
        }

        return $item;
    }

    /**
     * @param string|array $name
     * @param $default
     * @param $filter
     * @return array|mixed|null
     */
    public function get($name = '', $default = null, $filter = '')
    {
        if (is_array($name)) {
            return $this->only($name, $_GET, $filter);
        }

        return $this->input($_GET, $name, $default, $filter);
    }

    /**
     * @param string|array $name
     * @param $default
     * @param $filter
     * @return array|mixed|null
     */
    public function post($name = '', $default = null, $filter = '')
    {
        if (is_array($name)) {
            return $this->only($name, $_POST, $filter);
        }

        return $this->input($_POST, $name, $default, $filter);
    }

    /**
     * @param string|array $name
     * @param $default
     * @param $filter
     * @return array|mixed|null
     */
    public function delete($name = '', $default = null, $filter = '')
    {
        return $this->put($name, $default, $filter);
    }

    /**
     * @param string|array $name
     * @param $default
     * @param $filter
     * @return array|mixed|null
     */
    public function patch($name = '', $default = null, $filter = '')
    {
        return $this->put($name, $default, $filter);
    }

    /**
     * @param string|array $name
     * @param $default
     * @param $filter
     * @return array|mixed|null
     */
    public function put($name = '', $default = null, $filter = '')
    {
        if (is_array($name)) {
            return $this->only($name, $_POST, $filter);
        }

        return $this->input($_POST, $name, $default, $filter);
    }

    public function isAjax(): bool
    {
        $value = $_SERVER['HTTP_X_REQUESTED_WITH'] ?? '';
        return $value && 'xmlhttprequest' == strtolower($value);
    }
}